Tagless final
この辺は同義
tagless final
tagless final style_
tagless final encoding_
関数型言語にEDSLを作るパターンの1つ
型クラスを用いてDSLを作る
ASTのような中間表現を介さない
いくつかの記事を読んで具体例を見ると雰囲気がつかめるmrsekut.icon
tagless finalでDSLを定義する
DSLといえば、言語の定義とインタプリタが必要になるが、それらを型クラスを用いて表現する
型クラス
型に対する一連の操作
DSLの定義と言える
instance
特定の型ごとのDSLのインタプリタ
嬉しさ
https://keigoi.hatenadiary.org/entry/20111102/1320222417
通常のASTを定義するものと異なり中間表現を介さない
中間表現を介さないが、型クラスを使うことで、柔軟性を落とさずに表現できる
参考
Tagless Final Encoding in Haskell | Juan Pablo Royo Sales
Tagless Finalの概要と、類例との比較
具体的な小さいコードでの説明
Haskell+タグレスな型付きDSLで楽々!C言語コード生成 - keigoiの日記
ASTを使って定義することとの差異
記事の最後のコードをIDEに貼り付けて、ちょっと時間をかけて読むことで雰囲気を掴める
Tagless-Final Style
Oleg Kiselyovによるtaglee finalの解説paper集
1つ1つが長いので読めていないmrsekut.icon
#WIP
真面目に理解するためには、Tagless-Final Styleを読むべき
だが、そこまでモチベがあるわけではないので全く読んでないmrsekut.icon
https://docs.google.com/presentation/d/1VhS8ySgk2w5RoN_l_Ar_axcE4Dzf97zLw1uuzUJQbCo/edit#slide=id.g575348a5eb_0_0
https://serokell.io/blog/introduction-tagless-final
時間かけて読む価値ありそう
関連リンクもめっちゃある
hs
https://www.reddit.com/r/haskell/comments/a40z4t/blog_introduction_to_tagless_final/
https://jproyo.github.io/posts/2021-03-17-encoding-effects-freer-simple.html
以下全てscala
https://gist.github.com/mmenestret/0b746cfd650796a639723ee74a3de302
itnialとfinalの違いを述べているが、Scalaなのでよくわからん
taglessとtagless finalって別物7日?
https://keigoi.hatenadiary.org/entry/20111206/haskell_tagless_dsl
https://nebuta.hatenablog.com/entry/2013/08/01/143926
https://speakerdeck.com/dcubeio/from-tagless-final-to-typed-final-program-transformations-in-the-final-style
https://logmi.jp/tech/articles/324186
https://qiita.com/yyu/items/a2debfcde8f1915d5083
https://qiita.com/yyu/items/377513f17fec536b562e
https://speakerdeck.com/aoiroaoino/da-bian-dayo-tagless-final-patan
https://xuwei-k.hatenablog.com/entry/20150806/1438823224
https://zenn.dev/yyu/articles/61799662c042ac
https://jproyo.github.io/posts/2019-02-07-practical-tagless-final-in-scala.html
用語の意味
initial encoding
https://serokell.io/blog/introduction-tagless-final#initial-encoding
圏論用語に由来
通常の代数的データ型で定義したASTのこと?
主にAST内の「関数」をどう型安全に表現するかが論点っぽい
tagged initial encoding
https://serokell.io/blog/introduction-tagless-final#initial-encoding
通常の代数的データ型で定義したAST
元のASTで、関数なのかどうかを判断するためにResult型が必要になる
tagがどうのこうの言っているのは記事中の、ResultにおけるIntResultとLambdaResultのこと
この2つの値コンストラクタのことをtagと呼んでいる
実行時にこのtagを見て条件分岐して実行するのでtaggedと呼ばれる
tagless initial encoding
https://serokell.io/blog/introduction-tagless-final#initial-encoding
GADTで定義したAST
GADTで定義することで上のResultのような型が無くても安全に実行できる
つまり、タグがない
だからtagless
final encoding
https://serokell.io/blog/introduction-tagless-final#final-encoding
圏論由来の言葉ではない
圏論的には、ここでのinitialもfinalも両方initialなのらしい
initialと区別してのfinalという命名らしい
わかりづれーーmrsekut.icon
もっと良い命名あっただろう
semantic algebra
https://jproyo.github.io/posts/2019-03-17-tagless-final-haskell.html
domainごとに型クラスを作っっておく
どれもMonadを継承させることで、同じdo式の中で組み合わせて使える
code:hs
requestData :: (Cache m, DataSource m) => UserName -> m DataResult
requestData userName = do
cache <- getFromCache userName
result <- case cache of
Just dataResult -> return dataResult
Nothing -> getFromSource userName
storeCache result
return result
これがDSLで記述されたコード
mが開かれている
どのようなmonadに対してもこのコードを使える
実際にどういう操作をしてほしいかはinstanceを定義すればいい
これは、インタプリタを定義していることに等しい
2次元の拡張性
水平
同じ型クラスに対して、別のinstanceを作る
垂直
新しく別の型クラスを作る
https://keigoi.hatenadiary.org/entry/20111102/1320222417
Application Monad
色々な概念を包含したものらしい
部分評価
partial evaluation
type-preserving
https://logmi.jp/tech/articles/324186
ホスト言語の型システムで型付けされる
高階抽象構文
parserを作らなくていい
それってtagless final固有の嬉しさなんか?
DSLってそういうものじゃないの?って思ったけど違うかmrsekut.icon
Scalaの話がよく出てくるmrsekut.icon
#??
何が嬉しい?
tagってなに?
Higher-Order Abstract Syntaxってなに
この記事内で、taglessであることのメリットに関するものとして挙げられている
exotic termがまずなにかわからん
https://keigoi.hatenadiary.org/entry/20111102/1320222417
対象言語の抽象構文木が型クラスで表現されており、 GADTやバリアント型を使っていない
特に「型付きの」DSLをGADT抜きで実装できている点が面白い
GADTにより、対象言語の型情報をうまくHaskellの型システムと結びつけて扱うことができ、型安全なインタプリタないしコンパイラを作ることができる
が、taglessな方法では中間表現を経由しないためにそもそもGADTが必要ない。
普通
抽象構文木を使う
GADTを使うこともある
GADTは型付きの抽象構文木を作るときに使ったりする
tagless
中間表現を使わない
型クラスを使う
GADTは不要
https://keigoi.hatenadiary.org/entry/20111102/1320222417
code:hs
class Lang (e :: * -> *) (array :: * -> *) | e -> array, array -> e where
binOp :: Op2 -> e D -> e D -> e D
unOp :: Op1 -> e D -> e D
iter :: e Int -> e Int -> e D -> (e Int -> e D -> e D) -> e D
int :: Integer -> e Int
double :: D -> e D
let_ :: String -> e a -> (e a -> e b) -> e b
at :: e Int -> e (array x) -> e x
通常のASTの一つ一つのパーツが型ではなく、関数で表現されている
通常ならこのASTの型Langに変換したあとに、それを処理するが、
a → Lang → b
taglesssの場合は、Langという中間表現が存在しないので、そこへの変換もそこからの変換もない
a → b
中間表現を介さずに直接変換することになる
関数の組み合わせが実際のASTみたいな感じになる
code:hs
tenTimes x =
iter (int 1) (int 10) (double 0.0) (\_ acc-> binOp Plus acc x)
ここがどれだけ書きやすいかは、Langのinstanceにする際の関数のinterfaceによる
ここまでできればもはやASTできてるようなものなので、そのまま直接実行できる
中間表現的な柔軟性の表現
元のでは、中間表現を介して、入力と出力の多様性を担保できた
taglessでは、型クラスを使うので、instanceを個別に作れば同様の多様性を担保できる
@lotz84_: Webサイトから画像をダウンロードするという実際のアプリケーションを意識して、Tagless-FinalとReaderTを使ってDependency Injectionを実現し、疎結合な設計を行う方法についての解説👀
https://t.co/cr8VjVH0rh